package tmhprediction.main;

import java.io.File;
import java.io.IOException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.io.FileUtils;

import tmhprediction.classification.TMHClassifier;
import tmhprediction.optimization.GridSearchSequential;
import util.CopyInternalFiles;
import util.arff.NewStructureArffGenerator;
import util.arff.WrapperCaller;

public class TMHProgram1 {
    
	private static final double CMIN = -3.0;
	private static final double CMAX = 1.0;
	private static final double GMIN = -11.0;
	private static final double GMAX = -7.0;
//	private static final double CMIN = -3.0;
//	private static final double CMAX = -3.0;
//	private static final double GMIN = -11.0;
//	private static final double GMAX = -11.0;
	private static final double CSTEP = 1.0;
	private static final double GSTEP = 1.0;
	
			
	private static final String configLvl1 = "features_manualglob21.conf";
	private static final String configLvl2 = "features_manual.conf";	
    
    private static final String separator = System.getProperty("file.separator");
    
    
    //Arguments
    private static int cacheSize = 40;
    private static boolean isSVM1 = false;
    
    private static void printUsage(Options options) {
        HelpFormatter helpFormatter = new HelpFormatter( );
        helpFormatter.setWidth( 120 );
        helpFormatter.printHelp("java -Xmx1G -jar program1.jar -cs 800 -n 0 -ng -p $WRAPPERPATH -s 2 -t $IMPBASE/0/ -c $IMPBASE/1/ -o $OUTIMP/0/", options);
        System.exit(0);
      }
    
    @SuppressWarnings("static-access")
	public static void main(String[] args) 
    {
		Options options = new Options();
		
		Option optSvmType = OptionBuilder.withArgName("s")
				.hasArg()
				.withDescription("Is a model being build for the first (1) or second (2) level?")
				.withArgName("1|2")
				.create("s");	
		optSvmType.setRequired(true);
		Option optModelName = OptionBuilder.withArgName("n")
				.hasArg()
				.withArgName("prefix")
				.withDescription("Base name for the model and associated files")
				.create("n");
		optModelName.setRequired(true);
		Option optTrainingSet = OptionBuilder.withArgName("t")
				.hasArg()
				.withArgName("path")
				.withDescription("Path to the TrainingSet")
				.create("t");
		optTrainingSet.setRequired(true);
		Option optCrossTrainingSet = OptionBuilder.withArgName("c")
				.hasArg()
				.withArgName("path")
				.withDescription("Path to the Crosstraining-Set")
				.create("c");
		optCrossTrainingSet.setRequired(true);
		Option optOutput = OptionBuilder.withArgName("o")
				.hasArg()
				.withArgName("path")
				.withDescription("Path to the Output-Folder")
				.create("o");
		optOutput.setRequired(true);
	    Option optionPythonScript = OptionBuilder
			    .withArgName("p")
			    .hasArg()
			    .withDescription("Location of pp2features.py")
			    .create("p");
	    optionPythonScript.setRequired(true);
//	    Option optSkipGridSearch = OptionBuilder
//	    		.withArgName("ng")
//			    .withDescription("Skrip grid search")
//			    .create("nogrid");

		    
		options.addOption(optTrainingSet);
		options.addOption(optCrossTrainingSet);
		options.addOption( optSvmType);
		options.addOption( optModelName);
		options.addOption(optOutput);
		options.addOption(optionPythonScript);
//		options.addOption(optSkipGridSearch);
		options.addOption( "ng", false, "skip grid search and use default parameters of C=1 and gamma = 0.01" );
		
		Option optCacheSize = OptionBuilder.withArgName("cs")
				.hasArg()
				.withDescription("Cache size used during training phase in MByte (default: 40)")
				.withArgName("integer")
				.create("cs");		
		
		options.addOption(optCacheSize);
		
		CommandLineParser parser = new PosixParser();
		CommandLine cmd  = null;
		try
		{
			cmd = parser.parse(options, args);
		} catch (ParseException e)
		{
			System.out.println(e.getMessage());
			printUsage(options);
			System.exit(1);
		}
		
		//Parse Arguments:
		if(Integer.valueOf(cmd.getOptionValue("s")) == 1)
		{
			isSVM1 = true;
		}
		String outputBasePath = cmd.getOptionValue("o");
		
			//Write configs files to something that is accessible to the wrapper
		String configPath = outputBasePath + "/configs/";
//		
//			CopyInternalFiles.copyFile(configPathInternal+separator+configLvl1, configPath +separator + configLvl1);
//			CopyInternalFiles.copyFile(configPathInternal+separator+configLvl2, configPath + separator  +configLvl2);
			
			CopyInternalFiles.copyConfigFilesToDirectory(configPath +separator + configLvl1, configPath + separator  +configLvl2);
			
			//Base name of the current model and all related files
		String prefix = cmd.getOptionValue("n");
		
		//	run Wrapper for SVM1
		try
		{
			// for trainingset
			// for imps
			NewStructureArffGenerator generator = new NewStructureArffGenerator(cmd.getOptionValue("t") + separator + "impStructure", cmd.getOptionValue("t") + separator + "impOutput", false, isSVM1);
			generator.generateArff();
			String name = "TrainingAllFeatures";
			if (isSVM1) {
			    name = "TrainingImpFeatures";
			}
			WrapperCaller.callWrapperNoDel(name, cmd.getOptionValue("o"), cmd.getOptionValue("t") + separator + "impOutput", configPath,cmd.getOptionValue("p"), isSVM1);
			
			if (isSVM1) {
			    	// for sols
			    	name = "TrainingSolFeatures";
			    	generator.setIncludeEnergy(isSVM1);
			    	generator.setResultPath(cmd.getOptionValue("t") + separator + "solOutput");
			    	generator.generateArff();
			    	WrapperCaller.callWrapperNoDel(name, cmd.getOptionValue("o"), cmd.getOptionValue("t") + separator + "solOutput", configPath,cmd.getOptionValue("p"), isSVM1);
			    
			    	// mergeArffs
			    	WrapperCaller.mergeWrapperOutput(cmd.getOptionValue("o") + separator + "TrainingImpFeatures.arff", cmd.getOptionValue("o") + separator + "TrainingSolFeatures.arff", cmd.getOptionValue("o") + separator + "TrainingAllFeatures.arff");
			}
			
			
			// for crosstrainingset
			
			// for imps
			generator = new NewStructureArffGenerator(cmd.getOptionValue("t") + separator + "impStructure", cmd.getOptionValue("t") + separator + "impOutput", false, isSVM1);
			generator.setResultPath(cmd.getOptionValue("c") + separator + "impOutput");
			generator.setStructPath(cmd.getOptionValue("c") + separator + "impStructure");
			generator.generateArff();
			name = "CrossAllFeatures";
			if (isSVM1) {
			    name = "CrossImpFeatures";
			} 
			WrapperCaller.callWrapperNoDel(name, cmd.getOptionValue("o"), cmd.getOptionValue("c") + separator + "impOutput", configPath,cmd.getOptionValue("p"), isSVM1);
			if (isSVM1) {
			    	// for sols
			    	name = "CrossSolFeatures";
			    	generator.setIncludeEnergy(isSVM1);
			    	generator.setResultPath(cmd.getOptionValue("c") + separator + "solOutput");
			    	generator.generateArff();
			    	WrapperCaller.callWrapperNoDel(name, cmd.getOptionValue("o"), cmd.getOptionValue("c") + separator + "solOutput", configPath,cmd.getOptionValue("p"), isSVM1);
			    
			    	// mergeArffs
			    	WrapperCaller.mergeWrapperOutput(cmd.getOptionValue("o") + separator + "CrossImpFeatures.arff", cmd.getOptionValue("o") + separator + "CrossSolFeatures.arff", cmd.getOptionValue("o") + separator + "CrossAllFeatures.arff");
			}	
		}
		catch(Exception e)
		{
			System.out.println("Error while creating arff files");
			e.printStackTrace();
			System.exit(1);
		}
		
		String trainArffFile = cmd.getOptionValue("o")+"/TrainingAllFeatures.arff";
		String crossTrainArffFile = cmd.getOptionValue("o")+"/CrossAllFeatures.arff";
		
		//	GridSearch
		double[] res;
		if(!cmd.hasOption("ng"))
		{
			res =GridSearchSequential.performGridSearch(CMIN, CMAX, GMIN, GMAX
					,CSTEP, GSTEP, trainArffFile, crossTrainArffFile, isSVM1, cacheSize);
			System.out.println("Best c: " + res[0] + " Best g: " + res[1]);
		}
		else
		{
			res = new double[3];
			res[0] = 0;
			res[1] = -6.6;
			System.out.println("Skipping grid search and using default parameters with a cost of 1 and a gamma of 0.01");
		}
		
		//Train SVM with best parameters and save model
		TMHClassifier classifier;
		try
		{
			classifier = new TMHClassifier(trainArffFile, Math.pow(2.0,res[1]),Math.pow(2.0, res[0]), cacheSize);
			classifier.saveModel(cmd.getOptionValue("o") + separator+ prefix + "_model.svm");
		}
		catch (Exception e)
		{
			System.out.println("Error during creation of final classifier");
	
			e.printStackTrace();
			System.exit(1);
		}	
		
		
		if (isSVM1)
		{
			try {
				FileUtils.write(new File(outputBasePath+separator+prefix+"_cutoff"), String.valueOf(res[2]));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("Best cutoff was: " + res[2]);
		}
		
		//Cleanup
		File confFolder = new File(configPath);	
		try {
			FileUtils.deleteDirectory(confFolder);
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
}
